(** Large arrays based on byte arrays

- [Octi-rawb.Bytes] module should not be used directly unless you know what you do.
- [Octi-rawb.Vector] module permits to construct one-dimensional vector.
*)



(** [Bytes] contains ... bytes.
Several [Bytes.t] can share the same byte buffer. Each [Bytes.t] carries
a reference to its byte buffer and an offset value.
A new byte buffer of size [n] is created using [Bytes.create n] command.
This command returns a [Bytes.t] value having an offset value equal to 0.

Byte alignment: we suppose that the system
allocates aligned byte arrays. The function [get_bbaddress]
permits to control the value of internal byte buffer address.

WARNING, the consequence of a misuse of [Bytes] module functions is probably a crash!*)
module Bytes :
(** {5 Element kind} *)


sig
  type t
  type info = [`Info_sizeof_int | `Info_sizeof_long | `Info_sizeof_intnat | `Info_Max_long | `Info_Min_long]
  (** The type [Bytes.t] is the type of byte arrays *)


  external create :           int -> t = "octi_rawb_bytes_create"
      (** [Bytes.create size] returns a new uninitialized byte array.
      *)
      
  external make_offset :   t -> int -> t = "octi_rawb_bytes_make_offset"
      (** [Bytes.make_offset b offset] returns a new byte array sharing its byte buffer with [b].
	  If [b] size is [s], then the new byte array size is [s] - [offset].
	  In principle [offset] is non negative.
	  Strictly only the sum of [offset] and [b] offset
	  should be non negative.
	  In fact, there is no control here.
      *)

  external get_bbaddress : t -> int = "octi_rawb_bytes_get_bbaddress"
      (** [Bytes.get_bbaddress b] returns the internal byte buffer address *)

  external get_bbsize :      t -> int = "octi_rawb_bytes_get_bbsize"
      (** [Bytes.get_bbsize b] returns the size of the bytebuffer referenced by [b] *)

  external get_bbrefcount :  t -> int = "octi_rawb_bytes_get_bbrefcount"
      (** [Bytes.get_refcount b] returns the number of reference counts on the byte buffer referenced by [b] *)

  external get_address : t -> int = "octi_rawb_bytes_get_address"
      (** [Bytes.get_address b] returns the internal data address *)

  val get_size :      t -> int
    (** [Bytes.get_size b] returns the size of the [b], i.e. the number of bytes *)
    
  val get_offset :    t -> int
    (** [Bytes.get_offset b] returns the offset of [b], i.e. the byte shift between the first
	accessible [b] byte
	and the first byte of its buffer byte *)
    
  external info : info -> int = "octi_rawb_bytes_info"

  module C :
  sig
    module Char :
    sig
      external set : t -> int -> char -> unit = "octi_rawb_bytes_c_char_set"
	(** [Bytes.C.Int.set b i c] puts the byte [c] at
	    [b] byte buffer address plus [b] offset plus [i].
	*)
      external get : t -> int -> char = "octi_rawb_bytes_c_char_get"
	(** [Bytes.C.Int.get b i] returns the char corresponding to the byte stored at
	    [b] byte buffer address plus [b] offset plus [i].
	*)
    end
    module Nativeint :
    sig
      external set : t -> int -> nativeint -> unit = "octi_rawb_bytes_c_nativeint_set"
	(** [Bytes.C.Int.set b i j] puts the n=2,4or8 bytes of [j] at
	    [b] byte buffer address plus [b] offset plus n*i.
	*)
      external get : t -> int -> nativeint = "octi_rawb_bytes_c_nativeint_get"
	(** [Bytes.C.Int.get b i] returns the nativeint corresponding to the sizeof(int) bytes stored at
	    [b] byte buffer address plus [b] offset plus sizeof(int)*i.
	    Warning, as all C int's cannot be represented inside an OCaml int, we use Nativeint.t type.
	*)
    end

    module Float64 :
    sig
      external set : t -> int -> float -> unit = "octi_rawb_bytes_c_float64_set"
	(** [Bytes.C.Float64.set b i f] puts the 8 bytes of [f] at
	    [b] byte buffer address plus [b] offset plus 8*i.
	*)

      external get : t -> int -> float = "octi_rawb_bytes_c_float64_get"
	(** [Bytes.C.Float64.get b i] returns the float corresponding to the 8 bytes stored at
	    [b] byte buffer address plus [b] offset plus 8*i.
	*)

    end
    module ComplexFloat64 :
    sig
      external set : t -> int -> Complex.t -> unit = "octi_rawb_bytes_c_complexfloat64_set"
	(** [Bytes.C.ComplexFloat64.set b i z] stores the 16 bytes representing [z],
	    at [b] byte buffer address plus [b] offset plus 16*i.
	*)

      external get : t -> int -> Complex.t = "octi_rawb_bytes_c_complexfloat64_get"
	(** [Bytes.C.ComplexFloat64.get b i] returns the complex number corresponding
	    to the 16 bytes stored at
	    [b] byte buffer address plus [b] offset plus 16*i.
	*)

    end
  end
end

module Vector :
(**
   This module implements one-dimensional arrays based on bytes.
   The implementation allows sharing of large arrays between
   OCaml code and C or Fortran numerical libraried.
   The [Octi_rawb.Vector] module is somewhat different than standard [Bigarray] module.
*)
sig
  exception Out_of_bounds
    (** The exception [Vector.Out_of_bounds] is raised if you attempt to use an index out of [first]..[last] *)
  exception Not_a_vector
    (** The exception [Vector.Not_a_vector] is raised if you attempt to construct a subvector from a subvector *)
  type kind = Vector | Subvector
  type t
  val bytes : t -> Bytes.t
    (** [Vector.bytes v] returns the raw [Bytes.t] containing data. *)
    
  val word_size : t -> int
    (** [Vector.word_size v] returns the [v] word size, i.e. the number of bytes representing one element. *)

  val first_index : t -> int
    (** [Vector.first_index v] returns the [v] lower allowable index. *)

  val last_index : t -> int
    (** [Vector.first_index v] returns the [v] higher allowable index. *)

  val length : t -> int
    (** [Vector.length v] returns the vector length, i.e. the number of allowable indices. *)

  val make_subvector : t -> int -> int -> int -> t
    (** [Vector.make_subvector v offset first last] returns a subvector [sv] from a vector
	- [sv] and [v] share the same data
	- The first element of [sv] has index [first] and corresponds to element of [v] having index [offset] + [first]

	Note, [v] should be a vector, not a subvector *)

  module Nativeint :
  sig
    val create : int -> int -> t
      (** [Vector.Int.create first last] creates a new unitialized vector of indices [first]..[last]. *)
      
    val set : t -> int -> nativeint -> unit
      (** [Vector.Int.set v i x] stores the int [x] at index [i] of vector [v].*)
      
    val mset : t -> (int * nativeint) list -> unit
      (** [Vector.Int.set v index_value_list] is a multiple store.

	  NOTE: if the same index appears twice or more, only the last value is taken in account.
      *)

    val get : t -> int -> nativeint
      (** [Vector.Int.get i x] returns the int stored at index [i] of vector [v].*)

    val fill_fun : t -> (int -> nativeint) -> unit
      (** [Vector.Int.fill_fun v f] fills the vector [v] with floats [f](first)..[f](last) *)

    val fill_linstep : t -> nativeint -> nativeint -> unit 
      (** [Vector.Int.fill_linsptep v start step] fills the vector [v] with regularly spaced ints [start] [start+step]... *)
 
    val make_linstep : int -> int -> nativeint -> nativeint -> t
      (** [Vector.Float64.make_linspace first last start step] creates a vector of indices [first]..[last] filled with regularly spaced int's [start] [start+step]... *)

    val fold_left : ('a -> nativeint -> 'a) -> 'a -> t -> 'a
      (** [Vector.Int.fold_left f start v] returns f (...(f  (f start first_element) second_element)...) last_element)*)

    val fold_right : (nativeint -> 'a -> 'a) -> t -> 'a -> 'a
      (** [Vector.Int.fold_right f v start] returns f (last_element (... (f  second_element (f first_element start))...))*)

    val to_list : t -> nativeint list
      (** [Vector.Int.to_list v] return the list [[first_element; ...; last_element]] *)
  end

  module Float64 :
  sig
    val create : int -> int -> t
      (** [Vector.Float64.create first last] creates a new unitialized vector of indices [first]..[last]. *)
      
    val set : t -> int -> float -> unit
      (** [Vector.Float64.set v i x] stores the float [x] at index [i] of vector [v].*)
      
    val mset : t -> (int * float) list -> unit
      (** [Vector.Float64.set v index_value_list] is a multiple store.

	  NOTE: if the same index appears twice or more, only the last value is taken in account.
      *)

    val get : t -> int -> float
      (** [Vector.Float64.get i x] returns the float stored at index [i] of vector [v].*)

    val fill_fun : t -> (int -> float) -> unit
      (** [Vector.Float64.fill_fun v f] fills the vector [v] with floats [f](first)..[f](last) *)

    val fill_linspace : t -> float -> float -> unit 
      (** [Vector.Float64.fill_linspace v x1 x2] fills the vector [v] with regularly spaced floats [x1]..[x2] *)
 
    val make_linspace : int -> int -> float -> float -> t
      (** [Vector.Float64.make_linspace first last x1 x2] creates a vector of indices [first]..[last] filled with regularly spaced floats [x1]..[x2] *)

    val fold_left : ('a -> float -> 'a) -> 'a -> t -> 'a
      (** [Vector.Float64.fold_left f start v] returns f (...(f  (f start first_element) second_element)...) last_element)*)

    val fold_right : (float -> 'a -> 'a) -> t -> 'a -> 'a
      (** [Vector.Float64.fold_right f v start] returns f (last_element (... (f  second_element (f first_element start))...))*)

    val to_list : t -> float list
      (** [Vector.Float64.to_list v] return the list [[first_element; ...; last_element]] *)
  end
  module ComplexFloat64 :
  sig
    val create : int -> int -> t
      (** [Vector.ComplexFloat64.create first last] creates a new unitialized vector of indices [first]..[last]. *)
      
    val set : t -> int -> Complex.t -> unit
      (** [Vector.ComplexFloat64.set v i x] stores the complex [x] at index [i] of vector [v].*)
      
    val mset : t -> (int * Complex.t) list -> unit
      (** [Vector.Float64.set v index_value_list] is a multiple store.

	  NOTE: if the same index appears twice or more, only the last value is taken in account.
      *)

    val get : t -> int -> Complex.t
      (** [Vector.Float64.get i x] returns the complex stored at index [i] of vector [v].*)

    val fill_fun : t -> (int -> Complex.t) -> unit
      (** [Vector.Float64.fill_fun v f] fills the vector [v] with complex numbers [f](first)..[f](last) *)

    val fold_left : ('a -> Complex.t -> 'a) -> 'a -> t -> 'a
      (** [Vector.ComplexFloat64.fold_left f start v] returns f (...(f  (f start first_element) second_element)...) last_element)*)

    val fold_right : (Complex.t -> 'a -> 'a) -> t -> 'a -> 'a
      (** [Vector.ComplexFloat64.fold_right f v start] returns f (last_element (... (f  second_element (f first_element start))...))*)

    val to_list : t -> Complex.t list
      (** [Vector.ComplexFloat64.to_list v] return the list [[first_element; ...; last_element]] *)
  end
  module Float32 :
  sig
    val create : int -> int -> t
      (** [Vector.Float32.create first last] creates a new unitialized vector of indices [first]..[last]. *)
      
    val set : t -> int -> float -> unit
      (** [Vector.Float32.set v i x] stores the float [x] at index [i] of vector [v].*)
      
    val mset : t -> (int * float) list -> unit
      (** [Vector.Float32.set v index_value_list] is a multiple store.

	  NOTE: if the same index appears twice or more, only the last value is taken in account.
      *)

    val get : t -> int -> float
      (** [Vector.Float32.get i x] returns the float stored at index [i] of vector [v].*)

    val fill_fun : t -> (int -> float) -> unit
      (** [Vector.Float32.fill_fun v f] fills the vector [v] with floats [f](first)..[f](last) *)

    val fill_linspace : t -> float -> float -> unit 
      (** [Vector.Float32.fill_linspace v x1 x2] fills the vector [v] with regularly spaced floats [x1]..[x2] *)
 
    val make_linspace : int -> int -> float -> float -> t
      (** [Vector.Float32.make_linspace first last x1 x2] creates a vector of indices [first]..[last] filled with regularly spaced floats [x1]..[x2] *)

    val fold_left : ('a -> float -> 'a) -> 'a -> t -> 'a
      (** [Vector.Float32.fold_left f start v] returns f (...(f  (f start first_element) second_element)...) last_element)*)

    val fold_right : (float -> 'a -> 'a) -> t -> 'a -> 'a
      (** [Vector.Float32.fold_right f v start] returns f (last_element (... (f  second_element (f first_element start))...))*)

    val to_list : t -> float list
      (** [Vector.Float32.to_list v] return the list [[first_element; ...; last_element]] *)
  end
  module ComplexFloat32 :
  sig
    val create : int -> int -> t
      (** [Vector.ComplexFloat32.create first last] creates a new unitialized vector of indices [first]..[last]. *)
      
    val set : t -> int -> Complex.t -> unit
      (** [Vector.ComplexFloat32.set v i x] stores the complex [x] at index [i] of vector [v].*)
      
    val mset : t -> (int * Complex.t) list -> unit
      (** [Vector.Float32.set v index_value_list] is a multiple store.

	  NOTE: if the same index appears twice or more, only the last value is taken in account.
      *)

    val get : t -> int -> Complex.t
      (** [Vector.Float32.get i x] returns the complex stored at index [i] of vector [v].*)

    val fill_fun : t -> (int -> Complex.t) -> unit
      (** [Vector.Float32.fill_fun v f] fills the vector [v] with complex numbers [f](first)..[f](last) *)

    val fold_left : ('a -> Complex.t -> 'a) -> 'a -> t -> 'a
      (** [Vector.ComplexFloat32.fold_left f start v] returns f (...(f  (f start first_element) second_element)...) last_element)*)

    val fold_right : (Complex.t -> 'a -> 'a) -> t -> 'a -> 'a
      (** [Vector.ComplexFloat32.fold_right f v start] returns f (last_element (... (f  second_element (f first_element start))...))*)

    val to_list : t -> Complex.t list
      (** [Vector.ComplexFloat32.to_list v] return the list [[first_element; ...; last_element]] *)
  end
end
